//
//  Framework for a raytracer
//  File: triangle.cpp
//
//  Created for the Computer Science course "Introduction Computer Graphics"
//  taught at the University of Groningen by Tobias Isenberg.
//
//  Authors:
//	  Zhe Sun
//
//  This framework is inspired by and uses code of the raytracer framework of 
//  Bert Freudenberg that can be found at
//  http://isgwww.cs.uni-magdeburg.de/graphik/lehre/cg2/projekt/rtprojekt.html 
//

#include "triangle.h"
#include "plan.h"
#include <iostream>
#include <math.h>

/************************** Triangle **********************************/
#if 0
Hit Triangle::intersect(const Ray &ray)
{
     // All code below is based on page79 of the book
  double a,b,c,d,e,f,g,h,i,j,k,l;
  a = v1.x - v2.x;    d = v1.x - v3.x;   g = ray.D.x;   j = v1.x - ray.O.x;
  b = v1.y - v2.y;    e = v1.y - v3.y;   h = ray.D.y;   k = v1.y - ray.O.y;  
  c = v1.z - v2.z;    f = v1.z - v3.z;   i = ray.D.z;   l = v1.z - ray.O.z;

  double ei_hf = e*i - h*f;
  double gf_di = g*f - d*i;
  double dh_eg = d*h - e*g;
  double ak_jb = a*k - j*b;
  double jc_al = j*c - a*l;
  double bl_kc = b*l - k*c;

  double M = a*ei_hf + b*gf_di + c*dh_eg;

  // computer gamma and judge if hit
  double gamma = (i*ak_jb + h*jc_al + g*bl_kc)/M;
  if (gamma < 0 || gamma > 1)
    return Hit::NO_HIT();

  // computer beta and judge if hit
  double beta = (j*ei_hf + k*gf_di + c*dh_eg)/M;
  if (beta < 0 || beta > (1 - gamma))
    return Hit::NO_HIT();

  // computer the distance
  double t = -(f*ak_jb + e*jc_al + d*bl_kc)/M;
  if ( t < 0 )
    return Hit::NO_HIT();

  // now, the ray can hit the triangle

  // cross multiply get the normal
  Vector a_b = v2 - v1;
  Vector a_c = v3 - v1;

  Vector N = a_b.cross(a_c);   
  return Hit(t, N.normalized());
}
#else
Hit Triangle::intersect(const Ray &ray)
{
    double u, v;
    Vector edge1, edge2, tvec, pvec, qvec;
    double det, invDet;
    
    edge1 = v2 - v1;
    edge2 = v3 - v1;
    pvec = ray.D.cross(edge2);
    
    det = edge1.dot(pvec);

   if(det > -.000001 && det < .000001)
   {
      return Hit::NO_HIT();
   }
   invDet = 1/det;

   tvec = ray.O - v1;

   u = tvec.dot(pvec) * invDet;
   if(u < 0 || u > 1)
   {
        return Hit::NO_HIT();
   }

   qvec = tvec.cross(edge1);

   v = ray.D.dot(qvec) * invDet;
   if(v < 0 || (u + v) > 1)
   {
        return Hit::NO_HIT();
   }
      
    
    double t = edge2.dot(qvec) * invDet;
    if(t < 0)
    {
        return Hit::NO_HIT();
   }
   
   Point hitPoint = ray.O + ray.D * t;
   
   // get interpolation normal
   Point ver[3];
   ver[0] = v1; ver[1] = v2; ver[2] = v3;
   Vector normal[3];
   normal[0] = n1; normal[1] = n2; normal[2] = n3; 
   
    double d0 = (v1 - hitPoint).length();
    double d1 = (v2 - hitPoint).length();
    double d2 = (v3 - hitPoint).length();

    // Our three points.. re-oriented so that 'a' is the farthest

    int a, b, c;

    if (d0 > d1 && d0 > d2)
    {
        a = 0;
        b = 1;
        c = 2;
    }
    else if (d1 > d0 && d1 > d2)
    {
        a = 1;
        b = 0;
        c = 2;
    }
    else // if (d2 > d0 && d2 > d1)
    {
        a = 2;
        b = 0;
        c = 1;
    }

    // For convenience

    Point pointA = ver[a];
    Point pointB = ver[b];
    Point pointC = ver[c];

    Vector normalA = normal[a];
    Vector normalB = normal[b];
    Vector normalC = normal[c];

    // Generate an edge plane from pointB -> pointC
    //
    // Note that a Plane is instantiated with (origin, vector)
    Vector normalTriangle = edge1.cross(edge2);
    Vector normalPlan = (pointC - pointB).cross(normalTriangle);
    
    Plan edgePlane(pointC, pointC + normalPlan.normalized() * 5);

    // Generate a ray that originates at pointA and points toward I
    //
    // A Ray is also instantiated like a plane (origin, direction)

    Ray rayInTriangle(pointA, (hitPoint - pointA).normalized());

    // Find the intersection of the ray and the edge plane

    double QDistance = (edgePlane.intersect(rayInTriangle)).t;
    Point pointQ = pointA + QDistance * (hitPoint - pointA).normalized();

    // Get the interpolated vector for pointQ

    double distanceBQ = (pointB - pointQ).length();
    double distanceBC = (pointB - pointC).length();
    Vector normalQ = normalB + (normalC - normalB) * (distanceBQ / distanceBC);

    // Finally, interpolate from q to pointA to find the normal for I

    double distanceQI = (pointQ - hitPoint).length();
    double distanceQA = (pointQ - pointA).length();
    Vector normalI = normalQ + (normalA - normalQ) * (distanceQI / distanceQA);   
     
   /*
   Vector n = n1 + u*(n2-n1) + v*(n3-n1);
   n.normalize();
   */
   Vector n = normalI.normalized();
   
    // judge the direction of N
    if (n.dot(ray.D) < 0){
      return Hit(t, n);
    } else {
      return Hit(t, -n);
    }
   //Vector nxx = edge1.cross(edge2);
   //return Hit(t, nxx.normalized());;
}

void Triangle::mapCoord(Point p, double* u, double* v){
  *u = 0.0;
  *v = 0.0;
}

void Triangle::updateMotionPostion(Vector v, double speed, double t){
  v1 += t * speed * v;
  v2 += t * speed * v;
  v3 += t * speed * v;
}

#endif